package csa

import (
	"fmt"
	"git.sr.ht/~mil/mobroute/src/db"
	"git.sr.ht/~mil/mobroute/src/logger"
)

func ExecuteCSAFinalization(
	mapStopToArrivalDetails map[string]ArrivalDetails,
	stopWalkcostsDest map[string]uint,
	maxWalkSeconds uint,
) ([]*db.Connection, error) {
	if destStop, err := CheapestArrivalDestination(
		mapStopToArrivalDetails,
		stopWalkcostsDest,
		maxWalkSeconds,
	); err != nil {
		return nil, err
	} else if _, exists := mapStopToArrivalDetails[destStop]; !exists {
		return nil, fmt.Errorf("Failure to route to generate valid route to destination stop <%v>", destStop)
	} else if mapStopToArrivalDetails[destStop].Connection == nil {
		return nil, fmt.Errorf("Failure to generate route, connection to destination stop never reached?")
	} else {
		connectionsPath := []*db.Connection{mapStopToArrivalDetails[destStop].Connection}

		for {
			if prependStopDetails, exists := mapStopToArrivalDetails[connectionsPath[0].FromSSIDU]; exists {
				//logger.Warnf("Building final CSA connectionsPath: %v (%d) => %v (%d)", connectionsPath[0], connectionsPath[0].FromST, prependStopDetails, prependStopDetails.FromST)

				if prependStopDetails.Connection == nil {
					// Origin stop
					break
				} else if prependStopDetails.Connection.FromST <= connectionsPath[0].FromST {
					connectionsPath = append([]*db.Connection{prependStopDetails.Connection}, connectionsPath...)
				} else {
					logger.Fatalf(
						"Problem calculating route - algorithm broken: (%v vs %v) somehow destination connectionsPath's time >= from stop connectionsPath (%v / %v)",
						prependStopDetails.Connection.FromST, connectionsPath[0].FromST, prependStopDetails, connectionsPath[0],
					)
				}
			} else {
				break
			}
		}
		return connectionsPath, nil
	}
}

func CheapestArrivalDestination(
	mapStopToArrivalDetails map[string]ArrivalDetails,
	stopWalkcosts map[string]uint,
	maxWalkSeconds uint,
) (string, error) {
	var (
		infinite                       = ^uint(0) >> 1
		cheapestDestinationArrivalTime = infinite
		cheapestDestinationArrivalStop = ""
	)

	for stop, arrivalDetails := range mapStopToArrivalDetails {
		if walkcostFromStop, walkcostExists := stopWalkcosts[stop]; !walkcostExists {
			return "", fmt.Errorf("Walkcost from destination stop %v doesn't exist in passed walkcosts map", stop)
		} else if arrivalDetails.Connection != nil && walkcostFromStop < maxWalkSeconds {
			routeToDestinationArrivalTime := arrivalDetails.Time + walkcostFromStop
			if routeToDestinationArrivalTime < cheapestDestinationArrivalTime {
				cheapestDestinationArrivalTime = routeToDestinationArrivalTime
				cheapestDestinationArrivalStop = stop
			}
		}
	}

	if cheapestDestinationArrivalStop == "" {
		return "", fmt.Errorf("Couldn't determine cheapest arrival destination, consider increasing max_walk_seconds or max_transfer_seconds")
	} else {
		return cheapestDestinationArrivalStop, nil
	}
}
